﻿/**	@file		lolix_plugin_enter.inl
 *	@date		(2011-04-24 13:53:23)/(2013-12-30 00:08:53)
 *-----------------------------------------------------------------------------
 *	@version	1.0.1.3
 *	@author		Nick Shallery	(nicknide@gmail.com)
 *	@copyright	YOU HAVE ALL LEFT WITH THIS CODE WHILE YOU KEEP COMMENT IN EVERY COPY.
 *-----------------------------------------------------------------------------
**/


#if			!defined(__LOliX__LOLIX_PLUGIN_ENTER__H__NICK__K_IN_G__)
#define		__LOliX__LOLIX_PLUGIN_ENTER__H__NICK__K_IN_G__

#if			!defined(__LOLIX__LOLIX_ERR__K_IN_G__)
	#include	"../lolix_err.h"
#endif	//	!defined(__LOLIX__LOLIX_ERR__K_IN_G__)

#if			!defined(__LOLIX__LOLIX_INSTANCE__K_IN_G__)
	#include	"../lolix_inst.h"
#endif	//	!defined(__LOLIX__LOLIX_INSTANCE__K_IN_G__)

#if			!defined(__LOLIX__LOLIX_TYPE__K_IN_G__)
	#include	"../lolix_type.h"
#endif	//	!defined(__LOLIX__LOLIX_TYPE__K_IN_G__)

#if			!defined(__LOLIX__TOY__STD_FUNCTIONAL__NICK__K_IN_G__)
	#include	"../toy/std_functional.hpp"
#endif	//	!defined(__LOLIX__TOY__STD_FUNCTIONAL__NICK__K_IN_G__)

#if			defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
	#include	<Windows.h>
	#define		__LOLIX_TMP_DEF__PROCESS_ENTER
	#define		__LOLIX_TMP_DEF__PROCESS_LEAVE
#else	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
	#define		__LOLIX_TMP_DEF__PROCESS_ENTER	__attribute__((constructor))
	#define		__LOLIX_TMP_DEF__PROCESS_LEAVE	__attribute__((destructor))
#endif	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)

namespace lolix{namespace plugin_enter{
#if			!defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
	__LOLIX_TMP_DEF__PROCESS_ENTER
	LOLIX_RETNV __PN__process_enter(void);
	typedef int _TInstType;
#else	//	!defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
	typedef HINSTANCE _TInstType;
	__LOLIX_TMP_DEF__PROCESS_ENTER
	LOLIX_RETNV __PN__process_enter(bool* need_call_thread, _TInstType hInst);
#endif	//	!defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)

	__LOLIX_TMP_DEF__PROCESS_LEAVE
	void __PN__process_leave(void);

	void __PN__thread_enter(void);
	void __PN__thread_leave(void);
	namespace _help
	{
		typedef decltype(0) _TZeroType;
		typedef struct __T_have_no_param* have_no_param;
		typedef struct __T_have_one_param* have_one_param;
		typedef struct __T_have_two_param* have_two_param;
		typedef struct __T_not_function_param* not_function_param;

		template<typename _TPE>
		class get_function_param_info
		{
			typedef typename toy::function_info<_TPE>::template argument_types<1>::type _T_second_param_type;
			typedef typename toy::function_info<_TPE>::template argument_types<0>::type _T_first_param_type;
			enum { _is_have_2nd_param = type::type_equal<void, _T_second_param_type>::value };
			enum { _is_have_1st_param = type::type_equal<void, _T_first_param_type>::value };
		public:
			enum { is_have_param = _is_have_1st_param, is_function = 1 };
			typedef typename type::type_choise<
				_is_have_2nd_param
				, typename type::type_choise<_is_have_1st_param, have_no_param, have_one_param>::type
				, have_two_param
			>::type param_count;
		};

		template<>
		class get_function_param_info<int>
		{
		public:
			enum { is_have_param = 0, is_function = 0 };
			typedef not_function_param param_count;
		};

		class try_call_proc
		{
		public:
			template<typename _TProc>
			static LOLIX_INLINE_CALL void invoke(_TProc proc)
			{
				return proc();
			}
			static LOLIX_INLINE_CALL void invoke(const _TZeroType&)
			{
				return;
			}
		};


		template<typename _TPE, typename _TPL, typename _TTE, typename _TTL>
		struct proc_dispatch
		{
			static LOLIX_INLINE_CALL void auto_set_need_call_thread(bool* need_call_thread, type_true){*need_call_thread = true;}
			static LOLIX_INLINE_CALL void auto_set_need_call_thread(bool* need_call_thread, type_false){*need_call_thread = false;}
			static LOLIX_INLINE_CALL LOLIX_RETNV try_process_enter(_TPE pe, bool* need_call_thread, _TInstType x)
			{
				return __try_process_enter(pe, need_call_thread, x, typename get_function_param_info<_TPE>::param_count());
			}

			static LOLIX_INLINE_CALL LOLIX_RETNV try_process_enter(_TPE pe, bool* need_call_thread)
			{
				return __try_process_enter(pe, need_call_thread, typename get_function_param_info<_TPE>::param_count());
			}

			template<typename _Tret, typename _Noused = void>
			struct call_process_enter
			{
				static LOLIX_INLINE_CALL LOLIX_RETNV invoke(_TPE pe, bool* need_call_thread, _TInstType x)
				{
					return pe(need_call_thread, x);
				}

				static LOLIX_INLINE_CALL LOLIX_RETNV invoke(_TPE pe, bool* need_call_thread)
				{
					return pe(need_call_thread);
				}

				static LOLIX_INLINE_CALL LOLIX_RETNV invoke(_TPE pe, _TInstType x)
				{
					return pe(x);
				}

				static LOLIX_INLINE_CALL LOLIX_RETNV invoke(_TPE pe)
				{
					return pe();
				}
			};

			template<typename _T_noused>
			struct call_process_enter<void, _T_noused>
			{
				static LOLIX_INLINE_CALL LOLIX_RETNV invoke(_TPE pe, bool* need_call_thread, _TInstType x)
				{
					pe(need_call_thread, x);
					return LOLIX_RETNV_OK;
				}

				static LOLIX_INLINE_CALL LOLIX_RETNV invoke(_TPE pe, bool* need_call_thread)
				{
					pe(need_call_thread);
					return LOLIX_RETNV_OK;
				}

				static LOLIX_INLINE_CALL LOLIX_RETNV invoke(_TPE pe)
				{
					pe();
					return LOLIX_RETNV_OK;
				}
			};

			static LOLIX_INLINE_CALL LOLIX_RETNV __try_process_enter(_TPE pe, bool* need_call_thread, _TInstType x, have_two_param)
			{
				return call_process_enter<decltype(pe(need_call_thread, x))>::invoke(pe, need_call_thread, x);
			}

			static LOLIX_INLINE_CALL LOLIX_RETNV __try_process_enter(_TPE pe, bool* need_call_thread, _TInstType x, have_one_param)
			{
				return __try_process_enter_with_one_param(pe, need_call_thread, x, typename toy::function_info<_TPE>::template argument_types<0>::type());
			}

			static LOLIX_INLINE_CALL LOLIX_RETNV __try_process_enter(_TPE pe, bool* need_call_thread, _TInstType x, have_no_param)
			{
				auto_set_need_call_thread(need_call_thread
					, typename type::type_choise<
						get_function_param_info<_TTE>::is_function || get_function_param_info<_TTL>::is_function
						, type_true
						, type_false
					>::type()
					);
				return call_process_enter<decltype(pe())>::invoke(pe);
			}

			static LOLIX_INLINE_CALL LOLIX_RETNV __try_process_enter(_TPE /*pe*/, bool* need_call_thread, _TInstType /*x*/, not_function_param)
			{
				auto_set_need_call_thread(need_call_thread
					, typename type::type_choise<
						get_function_param_info<_TTE>::is_function || get_function_param_info<_TTL>::is_function
						, type_true
						, type_false
					>::type()
					);
				return LOLIX_RETNV_OK;
			}

			static LOLIX_INLINE_CALL LOLIX_RETNV __try_process_enter(_TPE pe, bool* need_call_thread, have_two_param)
			{
				return call_process_enter<decltype(pe(need_call_thread, _TInstType()))>::invoke(pe, need_call_thread, _TInstType());
			}

			static LOLIX_INLINE_CALL LOLIX_RETNV __try_process_enter(_TPE pe, bool* need_call_thread, have_one_param)
			{
				return call_process_enter<decltype(pe(need_call_thread))>::invoke(pe, need_call_thread);
			}

			static LOLIX_INLINE_CALL LOLIX_RETNV __try_process_enter(_TPE pe, bool* need_call_thread, have_no_param)
			{
				auto_set_need_call_thread(need_call_thread
					, typename type::type_choise<
						get_function_param_info<_TTE>::is_function || get_function_param_info<_TTL>::is_function
						, type_true
						, type_false
					>::type()
					);
				return call_process_enter<decltype(pe())>::invoke(pe);
			}

			static LOLIX_INLINE_CALL LOLIX_RETNV __try_process_enter(_TPE pe, bool* need_call_thread, not_function_param)
			{
				auto_set_need_call_thread(need_call_thread
					, typename type::type_choise<
						get_function_param_info<_TTE>::is_function || get_function_param_info<_TTL>::is_function
						, type_true
						, type_false
					>::type()
					);
				return LOLIX_RETNV_OK;
			}

			static LOLIX_INLINE_CALL LOLIX_RETNV __try_process_enter_with_one_param(_TPE pe, bool* need_call_thread, _TInstType, bool*)
			{
				return call_process_enter<decltype(pe(need_call_thread))>::invoke(pe, need_call_thread);
			}

			static LOLIX_INLINE_CALL LOLIX_RETNV __try_process_enter_with_one_param(_TPE pe, bool*, _TInstType x, _TInstType)
			{
				return call_process_enter<decltype(pe(x))>::invoke(pe, x);
			}
		};
	}
}}


#if			defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
	#define	__LOLIX_DEF__LIB_NAME(libName)	L##libName
#else	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
	#define	__LOLIX_DEF__LIB_NAME(libName)	libName
#endif	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)

#define		__LOLIX_ENTER_LEAVE_DEFINE_LIB_INF(id, name)	\
	static const lolix::lx_u32 g_lid[] = {id};\
	static const lolix::lolix_plugin_param g_info =\
	{	(lolix::LOLIX_ITF_ID*)g_lid\
	,	lolix::lolix_plugin_param::_ver\
	,	LOLIX_CFG__CFG_TYPE\
	,	sizeof g_info\
	,	0\
	,	__LOLIX_DEF__LIB_NAME(name)\
	};\
	extern "C" const lolix::lolix_plugin_param* LOLIX_CALL	lxpLibInfo(void)\
	{\
		return &g_info;\
	}

#define		__LOLIX_DEF__PROC_ENTER(p_enter, p_leave, t_enter, t_leave)	\
	void lolix::plugin_enter::__PN__process_leave(void){return _help::try_call_proc::invoke(p_leave);}\
	void lolix::plugin_enter::__PN__thread_enter(void){return _help::try_call_proc::invoke(t_enter);}\
	void lolix::plugin_enter::__PN__thread_leave(void){return _help::try_call_proc::invoke(t_leave);}


#if			LOLIX_CFG__PLATFORM_WINDOWS

#define		LOLIX_DEF__MODULE_ENTER(p_enter, p_leave, t_enter, t_leave)		\
	__LOLIX_DEF__PROC_ENTER(p_enter, p_leave, t_enter, t_leave)\
	lolix::LOLIX_RETNV lolix::plugin_enter::__PN__process_enter(bool* need_call_thread, _TInstType hInst){return _help::proc_dispatch<decltype(p_enter), decltype(p_leave), decltype(t_enter), decltype(t_leave)>::try_process_enter(p_enter, need_call_thread, hInst);}


#if			!LOLIX_CFG__LINK_TO_CLIB && defined(LOLIX_DEF__CC_TYPE_MS)
	void operator delete(void*){}
#endif	//	!LOLIX_CFG__LINK_TO_CLIB && defined(LOLIX_DEF__CC_TYPE_MS)


#if			!LOLIX_CFG__LINK_TO_CLIB && defined(LOLIX_DEF__CC_TYPE_MS)
	extern "C" long _fltused = 0;
#endif	//	!LOLIX_CFG__LINK_TO_CLIB && defined(LOLIX_DEF__CC_TYPE_MS)

BOOL __stdcall EnterMain(HINSTANCE hInst, ULONG ul_reason_for_call, LPVOID /*lpReserved*/)
{
	switch ( ul_reason_for_call )
	{
	case DLL_PROCESS_ATTACH:
		bool need_call_thread;
		if ( LOLIX_CHECK_ERROR(lolix::plugin_enter::__PN__process_enter(&need_call_thread, hInst)) )
			return FALSE;
		if ( !need_call_thread )
			::DisableThreadLibraryCalls(hInst);

#if			!LOLIX_CFG__LINK_TO_CLIB && defined(LOLIX_DEF__CC_TYPE_MS)
		_fltused = 1;
#endif	//	!LOLIX_CFG__LINK_TO_CLIB && defined(LOLIX_DEF__CC_TYPE_MS)
		break;

	case DLL_THREAD_ATTACH:
		lolix::plugin_enter::__PN__thread_enter();
		break;

	case DLL_THREAD_DETACH:
		lolix::plugin_enter::__PN__thread_leave();
		break;

	case DLL_PROCESS_DETACH:
		lolix::plugin_enter::__PN__process_leave();
		break;
	}

	return TRUE;
}

#else	//	LOLIX_CFG__PLATFORM_WINDOWS
#define		LOLIX_DEF__MODULE_ENTER(p_enter, p_leave, t_enter, t_leave)		\
	__LOLIX_DEF__PROC_ENTER(p_enter, p_leave, t_enter, t_leave);\
	lolix::LOLIX_RETNV lolix::plugin_enter::__PN__process_enter(){bool need_call_thread=false;return _help::proc_dispatch<decltype(p_enter), decltype(p_leave), decltype(t_enter), decltype(t_leave)>::try_process_enter(p_enter, &need_call_thread);}

#endif	//	LOLIX_CFG__PLATFORM_WINDOWS

#define		LOLIX_DEF__PLUGIN_ENTER(id, name, p_enter, p_leave, t_enter, t_leave)	\
	__LOLIX_ENTER_LEAVE_DEFINE_LIB_INF(id, name);\
	LOLIX_DEF__MODULE_ENTER(p_enter, p_leave, t_enter, t_leave)

#undef		__LOLIX_TMP_DEF__PROCESS_ENTER
#undef		__LOLIX_TMP_DEF__PROCESS_LEAVE

#endif	//	!defined(__LOliX__LOLIX_PLUGIN_ENTER__H__NICK__K_IN_G__)

